package com.gotokeep.keep.composition.demo.renderer;

import android.content.Context;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

/**
 * @author xana/cuixianming
 * @version 1.0
 * @since 2018-04-28 09:50
 */
public class FilterRenderer implements Renderer {
    private int mProgramObject;
    private int mWidth;
    private int mHeight;
    private float videoSize[];
    private FloatBuffer mVertices;
    private ShortBuffer mTexCoords;
    private Context mContext;
    private int texId;
    private static String TAG = "FilterRenderer";
    private final float[] mVerticesData = {-1f, -1f, 0, 1f, -1f, 0, -1f, 1f, 0, 1f, 1f, 0};
    private final short[] mTexCoordsData = {0, 1, 1, 1, 0, 0, 1, 0};


    private int mIntensityLocation;
    private float mIntensity = 1.0f;
    private int mFilterColorLocation;
    private float[] mColor = new float[]{0.6f, 0.45f, 0.3f, 1.0f};

    public FilterRenderer(Context mContext) {
        this.mContext = mContext;

        mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mVertices.put(mVerticesData).position(0);

        mTexCoords = ByteBuffer.allocateDirect(mTexCoordsData.length * 2)
                .order(ByteOrder.nativeOrder()).asShortBuffer();
        mTexCoords.put(mTexCoordsData).position(0);
    }

    public void setViewport(int width, int height) {
        mWidth = width;
        mHeight = height;
    }

    public void setVideoSize(int width, int height) {
        videoSize = new float[]{ (float) width/mWidth, (float) height/mHeight};
    }

    @Override
    public void onInit(int ...inputTexId) {
        texId = inputTexId[0];
        comipleAndLinkProgram();

        GLES20.glClearColor(1f, 0, 0, 0f);
    }

    @Override
    public void onRelease() {
        if (mProgramObject >= 0) {
            GLES20.glDeleteProgram(mProgramObject);
        }
    }

    @Override
    public void onDrawFrame() {
        GLES20.glViewport(0, 0, mWidth, mHeight);

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glUseProgram(mProgramObject);

        GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertices);
        GLES20.glEnableVertexAttribArray(0);
        GLES20.glVertexAttribPointer(1, 2, GLES20.GL_SHORT, false, 0, mTexCoords);
        GLES20.glEnableVertexAttribArray(1);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
        int loc = GLES20.glGetUniformLocation(mProgramObject, "u_Texture");
        GLES20.glUniform1i(loc, 0);
        GLES20.glUniform1f(mIntensityLocation, mIntensity);
        GLES20.glUniform3fv(mFilterColorLocation, 1, FloatBuffer.wrap(mColor));
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        Log.d(TAG, "onDrawFrame");
    }

    private int loadShader(int shaderType, String shaderSource) {
        int shader;
        int[] compiled = new int[1];
        // Create the shader object
        shader = GLES20.glCreateShader(shaderType);
        if (shader == 0)
            return 0;
        // Load the shader source
        GLES20.glShaderSource(shader, shaderSource);
        // Compile the shader
        GLES20.glCompileShader(shader);
        // Check the compile status
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            return 0;
        }
        return shader;
    }

    private void comipleAndLinkProgram() {
        String vShaderStr = "attribute vec4 a_position;    \n"
                + "attribute vec2 a_texCoords; \n"
                + "varying vec2 v_texCoords; \n"
                + "void main()                  \n"
                + "{                            \n"
                + "   gl_Position = a_position;  \n"
                + "    v_texCoords = a_texCoords; \n"
                + "}                            \n";
        final String fShaderStr = "" +
                "#extension GL_OES_EGL_image_external : require\n" +
                " precision lowp float;\n" +
                "  \n" +
                "  varying highp vec2 v_texCoords;\n" +
                "  \n" +
                "  uniform samplerExternalOES u_Texture;\n" +
                "  uniform float intensity;\n" +
                "  uniform vec3 filterColor;\n" +
                "  \n" +
                "  const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);\n" +
                "  \n" +
                "  void main()\n" +
                "  {\n" +
                " 	//desat, then apply overlay blend\n" +
                " 	lowp vec4 textureColor = texture2D(u_Texture, v_texCoords);\n" +
                " 	float luminance = dot(textureColor.rgb, luminanceWeighting);\n" +
                " 	\n" +
                " 	lowp vec4 desat = vec4(vec3(luminance), 1.0);\n" +
                " 	\n" +
                " 	//overlay\n" +
                " 	lowp vec4 outputColor = vec4(\n" +
                "                                  (desat.r < 0.5 ? (2.0 * desat.r * filterColor.r) : (1.0 - 2.0 * " +
                "(1.0 - desat.r) * (1.0 - filterColor.r))),\n" +
                "                                  (desat.g < 0.5 ? (2.0 * desat.g * filterColor.g) : (1.0 - 2.0 * " +
                "(1.0 - desat.g) * (1.0 - filterColor.g))),\n" +
                "                                  (desat.b < 0.5 ? (2.0 * desat.b * filterColor.b) : (1.0 - 2.0 * " +
                "(1.0 - desat.b) * (1.0 - filterColor.b))),\n" +
                "                                  1.0\n" +
                "                                  );\n" +
                " 	\n" +
                " 	//which is better, or are they equal?\n" +
                " 	gl_FragColor = vec4( mix(textureColor.rgb, outputColor.rgb, intensity), textureColor.a);\n" +
                "  }";
//        String fShaderStr = "\n" +
//                "#extension GL_OES_EGL_image_external : require\n" +
//                "precision mediump float;                     \n"
//                +"uniform samplerExternalOES u_Texture; \n"
//                +"varying vec2 v_texCoords; \n"
//                + "void main()                                  \n"
//                + "{                                            \n"
//                + "  gl_FragColor = texture2D(u_Texture, v_texCoords) ;\n"
//                + "}                                            \n";
        int vertexShader;
        int fragmentShader;
        int programObject;

        int[] linked = new int[1];
        // Load the vertex/fragment shaders
        vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vShaderStr);
        fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fShaderStr);
        // Create the program object
        programObject = GLES20.glCreateProgram();
        if (programObject == 0)
            return;
        GLES20.glAttachShader(programObject, vertexShader);
        GLES20.glAttachShader(programObject, fragmentShader);
        // Bind vPosition to attribute 0
        GLES20.glBindAttribLocation(programObject, 0, "a_position");
        GLES20.glBindAttribLocation(programObject, 1, "a_texCoords");
        // Link the program
        GLES20.glLinkProgram(programObject);
        // Check the link status
        GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0);
        if (linked[0] == 0) {
            Log.e(TAG, "Error linking program:");
            Log.e(TAG, GLES20.glGetProgramInfoLog(programObject));
            GLES20.glDeleteProgram(programObject);
            return;
        }
        mProgramObject = programObject;
        mIntensityLocation = GLES20.glGetUniformLocation(programObject, "intensity");
        mFilterColorLocation = GLES20.glGetUniformLocation(programObject, "filterColor");
    }

    public void setTexId(int texId) {
        this.texId = texId;
    }
}
